package cn.com.codes.framework.security.filter;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;


public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {

	HttpServletRequest originRequest = null;
	
	public XssHttpServletRequestWrapper(HttpServletRequest request) {
		super(request);
		this.originRequest = request;
	}

	/**
	 * 覆盖getParameter方法，将参数名和参数值都做XSS过滤。
	 * 如果需要获得原始的值，则通过super.getParameterValues(name)来获取
	 * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
	 */
	@Override
	public String getParameter(String name) {
		String value = super.getParameter(xssEncode(name));
		if (value != null) {
			value = xssEncode(value);
//			value = HTMLEncode(value);
		}
		return value;
	}
	

		
	@Override
    public Map<String, String[]> getParameterMap() {
    	Map<String, String[]> parameterMap = super.getParameterMap();
    	Map<String, String[]> tempParameterMap = new HashMap<String, String[]>();
    	if (parameterMap != null) {
			for(Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
				String[] parameters = entry.getValue();
				String[] temParameters = new String[parameters.length];
				for (int i = 0; i < parameters.length; i++) {
					if (parameters[i] != null) {
						temParameters[i] = xssEncode(parameters[i]);
//						temParameters[i] = HTMLEncode(temParameters[i]);
					}
				}
				tempParameterMap.put(entry.getKey(), temParameters);
			}
		}
        return tempParameterMap;
    }
	
	/**
	 * 对一些特殊字符进行转义
	 */
	public static String HTMLEncode(String text) {
		final StringBuilder result = new StringBuilder();
		final StringCharacterIterator iterator = new StringCharacterIterator(text);
		char character = iterator.current();
		while(character != CharacterIterator.DONE) {
			if (character == '<') {
				result.append("&lt;");
			}else if (character == '>') {
				result.append("&gt;");
			}else if (character == '&') {
				result.append("&amp;");
			}else if (character == '\'') {
				result.append("&#39;");
			}else if (character == '\"') {
				result.append("&quot;");
			}else {
				// the char is not a special one
				// add it to the result as is
				result.append(character);
			}
			character = iterator.next();
		}
		return result.toString();
	}
	
	/**
	 * 覆盖getHeader方法，将参数名和参数值都做xss过滤。如果需要获得原始的值，则通过super.getHeaders(name)来获取
	 * getHeadernames也可能需要覆盖
	 */
	@Override
	public String getHeader(String name) {
		String value = super.getHeader(xssEncode(name));
		if (value != null) {
			value = xssEncode(value);
		}
		return value;
	}
	
	/**
	 * 将容易引起xss漏洞的半角字符直接替换成全角字符
	 * 目前xssProject对注入代码要求是必须开始标签和结束标签（如<script></script>）正确匹配才能解析，否则报错；因此只能
	 * 替换调xsProject换为自定义实现
	 * 
	 */
	private static String xssEncode(String s) {

		if (null == s || s.isEmpty()) {
			return s;
		}
		String result = stripXSS(s);
		if (null != result) {
			result = escape(result);
		}
		return result;
	}
	
	public static String escape(String s) {
		StringBuilder sb = new StringBuilder(s.length() + 16);
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			switch (c) {
			case '>':
				sb.append('＞');//全角大于号
				break;
			case '<':
				sb.append('＜');//全角小于号
				break;
			case '\'':
				sb.append('＇');//全角单引号
				break;
			/*case '\"':
				sb.append('＂');//全角双引号
				break;*/
			case '\\':
				sb.append('＼');//全角斜线
				break;
			/*case '%':
				sb.append('％');//全角百分号
				break;*/
			case '(':
				sb.append('（');//全角小括号
				break;
			case ')':
				sb.append('）');//全角小括号
				break;
			default:
				sb.append(c);
				break;
			}
		}
		return sb.toString();
	}
	
	private static String stripXSS(String value) {
		if (null != value) {
			// NOTE: It's highly recommended to use the ESAPI library and
			// uncomment the following line to
			// avoid encoded attacks.
			// value = ESAPI.encoder().canonicalize(value);
			// Avoid null characters
			value = value.replaceAll("", "");
			// Avoid anything between script tags
			Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid anything in a src='...' type of expression
			scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Remove any lonesome </script> tag
			scriptPattern = Pattern.compile("</script>",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Remove any lonesome <script ...> tag
			scriptPattern = Pattern.compile("<script(.*?)>",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid eval(...) expressions
			scriptPattern = Pattern.compile("eval\\((.*?)\\)",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid expression(...) expressions
			scriptPattern = Pattern.compile("expression\\((.*?)\\)",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid javascript:... expressions
			scriptPattern = Pattern.compile("javascript:",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid vbscript:... expressions
			scriptPattern = Pattern.compile("vbscript:",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Avoid onload= expressions
			scriptPattern = Pattern.compile("onload(.*?)=",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");
			
			scriptPattern = Pattern.compile("<iframe>(.*?)</iframe>",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			
			scriptPattern = Pattern.compile("</iframe>",Pattern.CASE_INSENSITIVE);
			value = scriptPattern.matcher(value).replaceAll("");
			// Remove any lonesome <script ...> tag
			scriptPattern = Pattern.compile("<iframe(.*?)>",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
			value = scriptPattern.matcher(value).replaceAll("");			
		}
		return value;
	}
	
	/**
	 * 获取最原始的request
	 */
	public HttpServletRequest getOriginRequest() {
		return originRequest;
	}
	
	/**
	 * 获取最原始的request的静态方法
	 */
	public static HttpServletRequest getOriginRequest(HttpServletRequest request) {
		if (request instanceof XssHttpServletRequestWrapper) {
			return ((XssHttpServletRequestWrapper)request).getOriginRequest();
		}
		return request;
	}
}
